module DuckMap

  ##################################################################################
  # Config holds default values and attributes for sitemap and meta tags.
  # see {file:GUIDE.md#Default_values_and_attributes Guide}
  class Config
    include Attributes
    include SitemapObject

    ##################################################################################
    # Resets {.sitemap_attributes_hash} and {.attributes} to the defaults.
    #
    # @param [Symbol, String] name  The name of the class variable to reset.
    #                                 - :all - resets both variables.
    #                                 - :attributes - resets the {.attributes}
    #                                 - :sitemap_attributes_hash - resets the {.sitemap_attributes_hash}
    # @return [NilClass]
    def self.reset(name = :all)
      name = name.blank? ? :all : name.to_sym

      if name.eql?(:all) || name.eql?(:sitemap_attributes_hash)
        self.sitemap_attributes_hash = nil
      end

      if name.eql?(:all) || name.eql?(:attributes)
        self.attributes = nil
      end

      if name.eql?(:all) || name.eql?(:sitemap_attributes_defined)
        self.sitemap_attributes_defined = nil
      end

      return nil
    end

    ##################################################################################
    # This method needs to exist since {DuckMap::Attributes} is included in this class.
    #
    # See {DuckMap::Attributes#is_sitemap_attributes_defined?} for details.
    #
    # @return [TrueClass, FalseClass]
    def self.sitemap_attributes_defined

      if !defined?(@@sitemap_attributes_defined) || @@sitemap_attributes_defined.nil?
        @@sitemap_attributes_defined = false
      end

      return @@sitemap_attributes_defined
    end

    ##################################################################################
    # Setter method for sitemap_attributes_defined
    # @return [TrueClass, FalseClass]
    def self.sitemap_attributes_defined=(value)
      @@sitemap_attributes_defined = value
    end

    ##################################################################################
    # Class-level accessor method.  The value of the class variable @@sitemap_attributes_hash
    # is checked prior to returning a reference to it.  If the variable has not been defined or is nil,
    # then, it is initialized with default values.
    #
    # See {DuckMap::Attributes::ClassMethods#sitemap_attributes} for an explanation.
    #
    # @return [Hash]
    def self.sitemap_attributes_hash

      if !defined?(@@sitemap_attributes_hash) || @@sitemap_attributes_hash.nil?

        # many tests are sensitive to the values generated by this method.
        # changes to this structure will make tests fails until you edit
        # some of the .yml file(s) in the tests directory
        values = {
                  title: :title,
                  keywords: :keywords,
                  description: :description,
                  lastmod: :updated_at,
                  changefreq: nil,
                  priority: nil,
                  canonical: nil,
                  canonical_host: nil,
                  canonical_port: nil,
                  #compression: nil,      # don't need it here.
                  #sitemap_content: nil,  # don't need it here.
                  static_host: nil,
                  static_port: nil,
                  #static_target: nil,    # don't need it here.
                  url_format: nil,
                  url_limit: nil
                  }

        @@sitemap_attributes_hash = {}
        @@sitemap_attributes_hash[:default] = {}.merge(values)
        @@sitemap_attributes_hash[:default][:handler] = {action_name: :sitemap_index, first_model: false}

        @@sitemap_attributes_hash[:edit] = {}.merge(values)
        @@sitemap_attributes_hash[:edit][:handler] = {action_name: :sitemap_edit, first_model: true}

        @@sitemap_attributes_hash[:index] = {}.merge(values)
        @@sitemap_attributes_hash[:index][:handler] = {action_name: :sitemap_index, first_model: true}

        @@sitemap_attributes_hash[:new] = {}.merge(values)
        @@sitemap_attributes_hash[:new][:handler] = {action_name: :sitemap_new, first_model: true}

        @@sitemap_attributes_hash[:show] = {}.merge(values)
        @@sitemap_attributes_hash[:show][:handler] = {action_name: :sitemap_show, first_model: true}

      end

      return @@sitemap_attributes_hash
    end

    ###################################################################################
    # Returns a copy of the entire sitemap_attributes_hash Hash.
    # return [Hash]
    def self.copy_sitemap_attributes_hash
      values = {}

      # I actually have code to do a deep clone of a Hash, however, I can't release it right now.
      # I will in a later release.  For now, I will commit another sin.

      self.sitemap_attributes_hash.each do |item|
        values[item.first] = {}.merge(item.last)
        values[item.first][:handler] = {}.merge(item.last[:handler])
      end

      return values
    end

    ###################################################################################
    # Class-level accessor method.  Sets the value of the class variable @@sitemap_attributes_hash.
    # Setting the value to nil will cause it to be re-initialized on the next call to the get
    # method: class variable {.sitemap_attributes_hash}
    # @return [Hash]
    def self.sitemap_attributes_hash=(value)
      @@sitemap_attributes_hash = value
      return @@sitemap_attributes_hash
    end

    ###################################################################################
    # Returns the entire attributes Hash.
    # return [Hash]
    def self.attributes

      # set the default values.
      if !defined?(@@attributes) || @@attributes.nil?
        # set default values
        @@attributes = {
                        title: "Untitled",
                        keywords: nil,
                        description: nil,
                        lastmod: Time.now,
                        changefreq: "monthly",
                        priority: "0.5",
                        canonical: nil,
                        canonical_host: nil,
                        canonical_port: nil,
                        compression: :compressed,
                        sitemap_content: :xml,
                        static_host: nil,
                        static_port: nil,
                        static_target: nil,
                        url_format: "html",
                        url_limit: 50000
                        }
      end

      return @@attributes
    end

    ###################################################################################
    # Sets the entire attributes Hash.
    # return [Hash]
    def self.attributes=(value)
      @@attributes = value
    end

    ###################################################################################
    # Returns a copy of the entire attributes Hash.
    # return [Hash]
    def self.copy_attributes
      return {}.merge(self.attributes)
    end

    ###################################################################################
    # Performs a get or set on all of the key/values contained in the attributes class variable.
    # To get a value, simply call DuckMap::Config with the method name.
    #
    #    DuckMap::Config.title    # => Untitled
    #
    # To set a value, simply call DuckMap::Config with the method name and an assignment.
    #
    #    DuckMap::Config.title    # => Untitled
    #    DuckMap::Config.title = "My App"
    #    DuckMap::Config.title    # => My App
    #
    # I choose to go this route since this ONLY applies to DuckMap::Config and to reduce the amount of accessor
    # methods needed to get/set attributes.
    #
    # return [Object] Value stored via key.
    def self.method_missing(meth, *args, &block)
      value = nil


      if meth.to_s =~ /=$/
        key = meth.to_s
        key = key.slice(0, key.length - 1).to_sym
        self.attributes[key] = args.first
      else
        # all tests showed that meth is a Symbol
        value = self.attributes[meth]
      end

      return value
    end

  end

  ##################################################################################
  # A mixin the provide helper methods that gain access for configuration values from config/routes.rb.
  module ConfigHelpers
    extend ActiveSupport::Concern

    ##################################################################################
    # Sets the logging level.
    #
    #     # sets the logging level to :debug and full stack traces for exceptions.
    #     MyApp::Application.routes.draw do
    #       log_level :debug, full: true
    #     end
    #
    # @param [Symbol] value The logger level to use.  Valid values are:
    #                       - :debug
    #                       - :info
    #                       - :warn
    #                       - :error
    #                       - :fatal
    #                       - :unknown
    # @param [Hash] options Options Hash.
    # @option options [Symbol] :full Including full: true will include full stack traces for exceptions.
    #                          Otherwise, stack traces are stripped and attempt to only include application traces.
    def log_level(value, options = {})
      DuckMap::Logger.log_level = value
      if options.has_key?(:full)
        DuckMap.logger.full_exception = options[:full]
      end
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def title(value)
      Config.title = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def keywords(value)
      Config.keywords = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def description(value)
      Config.description = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def lastmod(value)
      Config.lastmod = LastMod.to_date(value)
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def changefreq(value)
      Config.changefreq = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def priority(value)
      unless value.blank?
        Config.priority = value.to_s
      end
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def url_format(value)
      Config.url_format = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def canonical(value)
      Config.canonical = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def canonical_host(value)
      Config.canonical_host = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def canonical_port(value)
      Config.canonical_port = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def compression(value)
      Config.compression = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def sitemap_content(value)
      Config.sitemap_content = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def static_host(value)
      Config.static_host = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def static_port(value)
      Config.static_port = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def static_target(value)
      Config.static_target = value
    end

    #################################################################################
    # See {DuckMap::ConfigHelpers} Overview for details.
    def url_limit(value)
      Config.url_limit = value
    end

    #################################################################################
    # See {DuckMap::SitemapObject::ClassMethods#acts_as_sitemap}
    def acts_as_sitemap(*args)
      Config.acts_as_sitemap(*args)
    end

    #################################################################################
    # See {DuckMap::SitemapObject::ClassMethods#sitemap_handler}
    def sitemap_handler(*args)
      Config.sitemap_handler(*args)
    end

    #################################################################################
    # See {DuckMap::SitemapObject::ClassMethods#sitemap_segments}
    def sitemap_segments(*args)
      Config.sitemap_segments(*args)
    end

  end

end
